---
title: Internationalization
order: -1
subGroup: advanced
---

# Internationalization

i18n support can be provided by theme. For example, [the official theme(vite-pages-theme-doc)](/official-theme) supports i18n. This document shows how to create a multi-lingual site with it.

## Define i18n metadata in the theme config

The [the official theme(vite-pages-theme-doc)](/official-theme) accepts 1i8n config like this:

```tsx
import { createTheme } from 'vite-pages-theme-doc'

export default createTheme({
  i18n: {
    defaultLocale: 'en',
    topBarLocaleSelector: true,
    locales: {
      en: {
        label: 'English',
        lang: 'en', // this will be set as the `lang` attribute on <html>
        routePrefix: '/',
      },
      zh: {
        label: '中文',
        lang: 'zh-CN',
        routePrefix: '/zh',
      },
    },
  },
  /** Other configs... */
})
```

Here is the type definition of the `i18n` property (`I18nConfig`):

```ts
interface I18nConfig {
  /**
   * The localeKey of default locale
   * If a page have pagePath that doesn't match any `LocalConfig.routePrefix`,
   *  the `I18nConfig.defaultLocale` will apply to it
   */
  defaultLocale: string
  /**
   * If true, this theme will render a locale selector at topbar
   * Only matters when you have more than one locales
   * @defaultValue true
   */
  topBarLocaleSelector?: boolean
  /**
   * Define all locales that your site supports
   * Map localeKey to locale config
   */
  locales: Record<string, LocalConfig>
}

interface LocalConfig {
  /**
   * This will be set as the lang attribute on <html>
   */
  lang?: string
  /**
   * This label will be used when rendering the locale
   * in the locale selector
   */
  label?: string
  /**
   * If a page have pagePath with this prefix, this locale will apply to it
   * If a page have pagePath that doesn't match any routePrefix,
   *  the `I18nConfig.defaultLocale` will apply to it
   */
  routePrefix?: string
}
```

With this i18n metadata, the theme can decide which locale to apply. For any specific page, it matches its pagePath with each `LocalConfig.routePrefix`:

- If the pagePath matches with a `LocalConfig.routePrefix`, this locale will apply to the page
- If the pagePath doesn't match any routePrefix, the `I18nConfig.defaultLocale` will apply to the page

With the example config above, page `/foo$.tsx` will have the locale keyed with `en`. Page `/zh/foo$.tsx` will have the locale keyed with `zh`.

What does it mean when we say "the page P has locale L" (or "locale L applies to page P")? It means two things:

- Most site-level theme configs can decide their value based on the currently applied locale. So that you can render `topNavs` and `sideNavs` with the correct language.
- In any React Component, you can get the currently applied locale from `useThemeCtx()` so that you can decide which i18n translated message to render.

We will talk about these techniques in the following sections.

## Return theme config according to the current locale

All [theme config](/official-theme#theme-configs) (`topNavs`, `sideNavs`, `logo`, etc.) **that accepts a value** can also accept a config function, so that you can get the current locale info from the function argument(`ThemeContextValue`). For example, here is the type definition of the theme config `topNavs`:

```ts
interface ThemeConfig {
  /** ... */
  topNavs?:
    | ReadonlyArray<MenuConfig>
    | ((ctx: ThemeContextValue) => ReadonlyArray<MenuConfig> | null | undefined)
}

type ThemeContextValue = {
  /** ... */
  resolvedLocale: {
    locale?: LocalConfig
    localeKey?: string
    pagePathWithoutLocalePrefix?: string
  }
}
```

To make topNavs support multiple languages, you can define `topNavs` like this:

```ts
import { createTheme } from 'vite-pages-theme-doc'
import type { MenuConfig } from 'vite-pages-theme-doc'

const topNavsConfig: { [locale: string]: MenuConfig[] } = {
  en: [
    { label: 'Home', path: '/' },
    {
      label: 'Guide',
      path: '/guide/introduce',
    },
    {
      subMenu: 'Links',
      children: [
        {
          label: 'Resources',
          path: '/resources',
        },
        {
          label: 'Vite',
          href: 'https://vitejs.dev/',
        },
      ],
    },
  ],
  zh: [
    { label: '首页', path: '/zh' },
    {
      label: '指南',
      path: '/zh/guide/introduce',
    },
    {
      subMenu: '链接',
      children: [
        {
          label: '资源',
          path: '/zh/resources',
        },
        {
          label: 'Vite',
          href: 'https://vitejs.dev/',
        },
      ],
    },
  ],
}

export default createTheme({
  /** ... */
  topNavs: ({ resolvedLocale: { localeKey } }) => {
    // return config according to resolvedLocale
    return topNavsConfig[localeKey!]
  },
})
```

> [Here is a complete example project](https://github.com/vitejs/vite-plugin-react-pages/blob/1a67c7c41f6fb488243183bf6814550d85ce5c23/packages/playground/use-theme-doc/pages/_theme.tsx#L27).

## Create pages for each locale

Your project file structure should be like this:

```txt
docs
├─ foo$.md
├─ bar$.tsx
├─ nested
│  └─ baz$.md
└─ zh
   ├─ foo$.md
   ├─ bar$.tsx
   └─ nested
      └─ baz.md
```

Each page with the default locale should have its translated version. For example, `/zh/foo$.md` should be the Chinese-translated version of `/foo$.md`.

### Markdown pages: translate them into each locale

Here is a markdown page for the default locale (`/foo$.md`):

```md
---
title: This is the title written in the default locale
---

This is the markdown content written in the default locale.
```

Here is the translated markdown page (`/zh/foo$.md`):

```md
---
title: This is the translated title
---

This is the translated markdown content.
```

### React Component pages: support all locales within them

If you define a page with React Component (`.tsx`), it is recommended to make it support all locales, instead of defining a new React Component for each locale.

For example, here is a React Component page for default locale (`/bar$.tsx`):

```tsx
/**
 * @title This is the title written in default locale
 */

import React from 'react'
import { useThemeCtx } from 'vite-pages-theme-doc'

const Page = () => {
  const intl = useIntl()
  return <p>{messages['page.page1.content']}</p>
}

/**
 * In a more complex app, you can use i18n library like `react-intl`
 * to make a React Component support multiple languages
 */
function useIntl() {
  const {
    resolvedLocale: { localeKey },
  } = useThemeCtx()
  return messages[localeKey]
}

const messages = {
  en: {
    'page.page1.content': 'This is Page1 content',
  },
  zh: {
    'page.page1.content':
      'This is translated Page1 content for the locale `zh`',
  },
}

export default Page
```

When defining the page for the other locale (`/zh/bar$.tsx`), you can **re-export the React Component**:

```tsx
/**
 * @title This is the translated title for the locale `zh`
 */

export { default } from '../bar$'
```

Notice that we reuse the same React Component for multiple locales. And we define page static metadata(`@title`) with correct translation in each page file.

> When using i18n library like `react-intl`, and you need to wrap your App with some custom React Component, you can use `ThemeConfig.AppWrapper`.

### Get the current locale in React Components or Hooks

```tsx
import { useThemeCtx } from 'vite-pages-theme-doc'

function useI18nMeta() {
  const {
    resolvedLocale,
    themeConfig: { i18n },
  } = useThemeCtx()
  // get resolvedLocale and i18nConfig from `useThemeCtx()`
  return { resolvedLocale, i18nConfig: i18n }
}
```
